/*
 *  RefExp.cpp
 *  gre
 *
 *  Created by Kutlak Roman on 26/07/2011.
 *  Copyright 2011 University of Aberdeen. All rights reserved.
 *
 */

#include <iostream>
#include <sstream>

#include "RefExp.h"
#include "Ontology.h"

using std::string;
using std::cout;
using std::cerr;
using std::endl;

namespace kutlak {
namespace roman {
namespace gre {
    
    
#ifdef DEBUG
    std::once_flag RefExp::sOntologyFlag;
#endif
    /////////////////////////////// PUBLIC   ///////////////////////////////////
    
#pragma mark -
#pragma mark lifecycle
    RefExp::RefExp(const EntityPtrT& refPtr) :
    mReferent(refPtr) {} // RefExp
    
    RefExp::RefExp(const RefExp& from) : 
    mProperties(from.mProperties),
    mReferent(from.mReferent) {} // RefExp
    
    RefExp::RefExp(RefExp&& from) : 
    mProperties(std::move(from.mProperties)),
    mReferent(std::move(from.mReferent)) {} // RefExp
    
    RefExp::~RefExp() = default;
    
    RefExp& RefExp::operator=(RefExp rhs)
    {    
        swap(*this, rhs);   
        return *this;
    }
    
    void swap(RefExp& lhs, RefExp& rhs)
    {
        std::swap(lhs.mProperties, rhs.mProperties);
        std::swap(lhs.mReferent, rhs.mReferent);
    }
    
#pragma mark -
#pragma mark operators

    
    bool RefExp::operator==(const RefExp& rhs) const 
    {
        if (mProperties.size() != rhs.mProperties.size())
            return false;
        
        typedef std::list<Property>::const_iterator itT;
        
        std::list<Property> e1Sorted = mProperties;
        e1Sorted.sort();
        
        std::list<Property> e2Sorted = rhs.mProperties;
        e2Sorted.sort();
        
        itT first1 = e1Sorted.begin();
        itT last1  = e1Sorted.end();
        itT first2 = e2Sorted.begin();
        itT last2  = e2Sorted.end();
        
        while (first1!=last1 && first2!=last2) 
        {
            if (*first1++ != *first2++)
                return false;
        }
        
        if (last1 != e1Sorted.end() ||
            last2 != e2Sorted.end())
            return false;
        
        return true;
    } // ==
    
    bool RefExp::operator!=(const RefExp& rhs) const 
    {
        return !(*this == rhs);
    }
    
    
#pragma mark -
#pragma mark operations
    //=============================  OPERATIONS  ===============================
    
    string RefExp::ToString() const 
    {
        std::stringstream ss;
//        ss << "RefExp: ";
        for (auto it = mProperties.begin();
             it != mProperties.end(); ++it) 
        {
            it->ToRdf(ss) << " ";
        }

        return ss.str();
    }
    
    bool RefExp::Add(const Property& p)
    {                
        for (auto it = mProperties.begin();
             it != mProperties.end(); ++it) 
        {
            if (p == *it)
                return false;
            
            // avoid including two types in the same hierarchy
            pOntology o = Ontology::Default();
            if (o) 
            {
                if (Ontology::kType == p.Attribute() && 
                    Ontology::kType == it->Attribute()) 
                {
                    if (o->Subsumes(p, *it)) 
                    {
                        return false;
                    } 
                    else if (o->Subsumes(*it, p)) // TODO: test if removing the element works..
                    {
                        it = mProperties.erase(it);
                    }
                }
            }
            else 
            {
#ifdef DEBUG
                std::call_once(RefExp::sOntologyFlag, []()
                {
                    cout << "!!!!!! Warning! RefExp::Add()\
                    ---> Ontology not set ^^ ?" << '\n';
                });
#endif 
            }
        }
        
        mProperties.push_back(p);
        return true;
    }
    
    bool RefExp::Add(const RefExp& properties) 
    {
        bool result = true;
        for (auto it = properties.begin();
             it != properties.end(); ++it) 
        {
            result &= Add(*it);
        }
        return result;
    }
    
    bool RefExp::Prepend(const Property& p) 
    {
        for (auto it = mProperties.begin();
             it != mProperties.end(); ++it)
            if (p == *it)
                return false;
        
        mProperties.push_front(p);
        return true;
    }
    
    void RefExp::Clear() 
    {
        mProperties.clear();
    }
    
    void RefExp::Sort() 
    {
        mProperties.sort();
    }
    
#pragma mark -
#pragma mark inquiry
    //=============================  INQUIRY     ===============================
    
    
    bool RefExp::HasDeterminer() const 
    {
        for (auto& p : mProperties)
            if (p.AttributeStr() == "determiner")
                return true;
        
        return false;
    }

}
}
}
